/*
* GeoVISTA Center (Penn State, Dept. of Geography)
* Copyright (c), 2003-2011,  Jin Chen and Alan M. MacEachren, GeoVISTA Center, Penn State University
* All Rights Researved.
*
*
* @author: jin Chen
* @date: Oct 16, 2003$
* @version: 1.0
*/
package edu.psu.geovista.app.pcp.animation;

import edu.psu.geovista.app.pcp.animation.event.AnimationEvent;
import edu.psu.geovista.app.pcp.animation.event.AnimationListener;
import edu.psu.geovista.app.pcp.plot.control.tools.DataSelectTool;
import edu.psu.geovista.app.pcp.toolkits.collection.CollectionUtils;
import edu.psu.geovista.classification.CategoryList;
import edu.psu.geovista.data.model.AppDataModel;
import edu.psu.geovista.ui.event.SelectionEvent;
import edu.psu.geovista.ui.event.SelectionListener;

import javax.swing.*;
import java.util.ArrayList;
import java.util.HashSet;

public class DataAnimator extends JPanel
        implements AnimationListener,SelectionListener{
    public static final boolean DEBUG = false;
    //GUI
    static final String DataWhole="Whole Data";
    static final String DataCategory="Category";
    static final String DataSelection="Selected";

    public static final String AllAttribute="All";

    static final String BhIterative="Iterative paint"; //accumulatively
    static final String BhOrderStrum="Ordered Strum";//one by one

    public static final String SORT_Ascend="ascend";
    public static final String SORT_descend="descend";

    protected  ArrayList dataItems=new ArrayList();//={"Whole Data","","Selected"};
    protected  String[] attributes;//=new ArrayList();
    public static final int delayCoff=10;

    String[] attributeNames;
    boolean iterative=false;

    Object dataDomain;
    int[] ids;//null mean invalid
    boolean newSession=false;

    protected HashSet lastSelection;
    //model
    //DefaultPlotModel model;

    //logic component
    protected DataProcessor dataPrecessor;
    protected AnimationWorker animator;
    protected AnimationResponder responder;//In this case, it is DataSelectCmd

    private CategoryList ctgList;

    public DataAnimator() {
        initComponents();

    }
    private void initValues(AppDataModel model){
        //data
        //dataItems.add("Whole Data");
        //dataItems.add("Selected");//,"","Ca"};
        String[] dataD=new String[]{DataWhole,DataSelection};//,DataSelection,DataCategory};
        dataCB.removeAllItems() ;

        for (int i=0;i<dataD.length ;i++){
            dataCB.addItem(dataD[i]);
        }

        //category
        ctgCB.removeAllItems() ;
        ctgCB.setEditable(false);
        if(this.ctgList!=null){
            //unfinished
            //ctgCB.addItem()
        }


        //variable List
        attributes=model.getColumnNames();
        varListCB.removeAllItems() ;
        //this.varListCB.addItem(AllAttribute);
        Object selectVar=varListCB.getSelectedItem() ;
        for (int i=0;i<attributes.length ;i++){
            this.varListCB.addItem(attributes[i]);
        }
        if(selectVar==null)
            varListCB.setSelectedIndex(0);
        else{
            varListCB.setSelectedItem(selectVar) ;
        }


        //Strategy
        behaviorCB.removeAllItems() ;
        this.behaviorCB.addItem(BhOrderStrum);
        this.behaviorCB.addItem(BhIterative);

        if(this.iterative ){
            this.behaviorCB.setSelectedItem(BhIterative);
        }
        else{
            this.behaviorCB.setSelectedItem(BhOrderStrum);
        }

        //sort
        sortCB.removeAllItems() ;
        this.sortCB.addItem(SORT_Ascend);
        this.sortCB.addItem(SORT_descend);

        dataPrecessor=new DataProcessor();
        dataPrecessor.setModel(model);

        this.animator =new AnimationWorker();
        this.animator.addAnimationListener(this);
        feedDataToAnimator();

        if(animator.isAscend() )
            this.sortCB.setSelectedItem(SORT_Ascend);
        else{
            this.sortCB.setSelectedItem(SORT_descend);
        }


    }
    /**
     *  listen to user manually selection
     * @param e
     */
    public void selectionChanged(SelectionEvent e) {
         DataSelectTool sel=(DataSelectTool) e.getSource() ;
         this.lastSelection =sel.getCopyOfLastSelection() ;
    }
    private void feedDataToAnimator(){
        if(dataPrecessor!=null&&animator!=null){
            String dataType=(String) this.dataCB.getSelectedItem() ;

            int ids[]=null;
            String attr=(String) this.varListCB.getSelectedItem() ;
            if(attr==null)return;//caused by  varListCB.removeAllItems()
            if(dataType.equals(DataWhole)){
                /*String attr=(String) this.varListCB.getSelectedItem() ;
                if(attr==null)return;//caused by  varListCB.removeAllItems()*/
                ids=this.dataPrecessor.getIndexOfSortedData(attr);
            }
            else if(dataType.equals(DataSelection) ){

                //DataSelectCmd sel=(DataSelectCmd) this.responder ;
                // 
                // 

                HashSet selection=this.lastSelection ;
                if(selection==null||selection.size() ==0) return;
                int[] selectedIds=CollectionUtils.convertToIntArray(selection);
                ids=this.dataPrecessor.getIndexOfSelectedData(selectedIds,attr);

            }
            this.animator.setData(CollectionUtils.convertArrayToList(ids));
            this.newSession =true;
        }
    }


    private void startAnimation(){

        /*if(ids==null){
        String attr=(String) this.varListCB.getSelectedItem() ;
        if(((String)dataDomain).equals(DataWhole)){
        ids=this.dataPrecessor.getIndexOfSortedData(attr);
        }
        }
        this.animator.setData(CollectionUtils.convertArrayToList(ids));*/
        this.animator.start() ;

    }
    public void animationChanged(AnimationEvent e) {
        int state=e.getState() ;
        float value=e.getValue() ;
        if(state==AnimationEvent.ANIMATION_ON ){
            if(newSession){
                //dp{
                if (DEBUG){
                    System.out.println("clean session");
                }//dp}
                this.responder.aboutToStartNewSessoin() ;
                newSession=false;
            }
            String bh=(String) this.behaviorCB.getSelectedItem();
            if(bh.equals(BhIterative) )
                responder.doAccumulativeAnimation(value);  //Interative
            else
                responder.doOneByOneAnimation(value);  //Ordered Strum
            if (DEBUG){
                System.out.println("animation value:"+value);
            }
        }
        else if(state==AnimationEvent.ANIMATION_AboutToStart ){
            if(newSession){
                System.out.println("clean layer");
                 feedDataToAnimator();
                newSession=true;
            }
        }
        else if(state==AnimationEvent.ANIMATION_Stoped ){
            this.newSession =true;
        }
        else if(state==AnimationEvent.ANIMATION_Paused ){

        }

    }
    /*******************************************************************************************************
     *                external handler: enable external client to control animation
     *******************************************************************************************************/
    public void start() {
        this.startBttn.doClick() ;
    }
    public void step() {

        this.stepBttn.doClick() ;
    }
    public void pause() {
        this.pauseBttn.doClick() ;
    }
    public void stop() {
        this.stopBttn.doClick() ;
    }
   /*******************************************************************************************************
    *                Property
    *******************************************************************************************************/
    public void setResponder(AnimationResponder responder) {
        this.responder = responder;
        DataSelectTool sel=(DataSelectTool) responder;
        sel.addInternalSelectionListener(this);
    }

    public void setModel(AppDataModel model) {
        //this.model = model;
        this.initValues(model) ;
    }

    /*******************************************************************************************************
     *                GUI
     *******************************************************************************************************/
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        delayP = new javax.swing.JPanel();
        delaySlider = new javax.swing.JSlider();
        delayF = new javax.swing.JTextField();
        strategyP = new javax.swing.JPanel();
        behaviorCB = new javax.swing.JComboBox();
        sortCB = new javax.swing.JComboBox();
        controlP = new javax.swing.JPanel();
        startBttn = new javax.swing.JButton();
        stepBttn = new javax.swing.JButton();
        pauseBttn = new javax.swing.JButton();
        stopBttn = new javax.swing.JButton();
        dataP = new javax.swing.JPanel();
        dataCB = new javax.swing.JComboBox();
        ctgCB = new javax.swing.JComboBox();
        varListCB = new javax.swing.JComboBox();

        setLayout(new java.awt.GridBagLayout());

        setBorder(new javax.swing.border.TitledBorder("Animation"));
        delayP.setLayout(new java.awt.FlowLayout());

        delayP.setBorder(new javax.swing.border.TitledBorder("Delay "));
        delaySlider.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                delaySliderStateChanged(evt);
            }
        });

        delayP.add(delaySlider);

        delayF.setPreferredSize(new java.awt.Dimension(40, 20));
        delayF.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                delayFActionPerformed(evt);
            }
        });

        delayP.add(delayF);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        add(delayP, gridBagConstraints);

        strategyP.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

        strategyP.setBorder(new javax.swing.border.TitledBorder("Strategy"));
        behaviorCB.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "iterative repaint" }));
        behaviorCB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                behaviorCBActionPerformed(evt);
            }
        });

        strategyP.add(behaviorCB);

        sortCB.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "ascend" }));
        sortCB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sortCBActionPerformed(evt);
            }
        });

        strategyP.add(sortCB);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        add(strategyP, gridBagConstraints);

        controlP.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

        startBttn.setText("Start");
        startBttn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                startBttnActionPerformed(evt);
            }
        });

        controlP.add(startBttn);

        stepBttn.setText("Step");
        stepBttn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                stepBttnActionPerformed(evt);
            }
        });

        controlP.add(stepBttn);

        pauseBttn.setText("Pause");
        pauseBttn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                pauseBttnActionPerformed(evt);
            }
        });

        controlP.add(pauseBttn);

        stopBttn.setText("Stop");
        stopBttn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                stopBttnActionPerformed(evt);
            }
        });

        controlP.add(stopBttn);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        add(controlP, gridBagConstraints);

        dataP.setLayout(new java.awt.FlowLayout());

        dataP.setBorder(new javax.swing.border.TitledBorder("Data for animation"));
        dataCB.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Whole Data" }));
        dataCB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                dataCBActionPerformed(evt);
            }
        });

        dataP.add(dataCB);

        ctgCB.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "category1" }));
        dataP.add(ctgCB);

        varListCB.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "all attributes" }));
        varListCB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                varListCBActionPerformed(evt);
            }
        });

        dataP.add(varListCB);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        add(dataP, gridBagConstraints);

    }

    private void dataCBActionPerformed(java.awt.event.ActionEvent evt) {
        // Add your handling code here:
        String o=(String) this.dataCB.getSelectedItem() ;
        //temp

        if(dataDomain==null){
            dataDomain=o;
        }
        else{
            if(!dataDomain.equals(o) ){
                this.ids =null;// data domain changed, need recalculate ids

            }
        }
        this.stop() ;


    }

    private void stopBttnActionPerformed(java.awt.event.ActionEvent evt) {
        if(animator!=null)
        this.animator.stop() ;
        // Add your handling code here:
    }

    private void pauseBttnActionPerformed(java.awt.event.ActionEvent evt) {
        // Add your handling code here:
        if(animator!=null)
        this.animator.pause() ;
    }

    private void stepBttnActionPerformed(java.awt.event.ActionEvent evt) {
        // Add your handling code here:
        if(animator!=null)
        this.animator.step();
    }

    private void startBttnActionPerformed(java.awt.event.ActionEvent evt) {
        // Add your handling code here:
        //this.animator.start();
        this.startAnimation() ;
    }

    private void sortCBActionPerformed(java.awt.event.ActionEvent evt) {
        // Add your handling code here:
        String sort=(String) this.sortCB.getSelectedItem() ;
        if(sort!=null&&animator!=null){
            if(sort.equals(SORT_Ascend) ){
                animator.setAscend(true);

            }
            else{
                animator.setAscend(false);
            }
        }
        this.stop() ;
    }

    private void behaviorCBActionPerformed(java.awt.event.ActionEvent evt) {
        // Add your handling code here:
        this.stop() ;
    }

    private void delayFActionPerformed(java.awt.event.ActionEvent evt) {
        // Add your handling code here:
    }

    private void delaySliderStateChanged(javax.swing.event.ChangeEvent evt) {
        // Add your handling code here:
        int delay=(this.delaySlider.getValue()*delayCoff);
        this.delayF.setText(String.valueOf(delay ));
        this.animator.setDelay(delay);
    }

    private void varListCBActionPerformed(java.awt.event.ActionEvent evt) {
        this.stop() ;
        // Add your handling code here:
        /* Object[] o= this.varListCB.getSelectedObjects() ;
        attributeNames=new String[o.length ];
        for (int i=0;i<o.length ;i++){
        attributeNames[i]=(String) o[i];
        }*/
        //this.feedDataToAnimator() ; //change variable, need re-calculate data for animator
    }


    // Variables declaration - do not modify
    private javax.swing.JPanel dataP;
    private javax.swing.JComboBox sortCB;
    private javax.swing.JPanel strategyP;
    private javax.swing.JButton stopBttn;
    private javax.swing.JSlider delaySlider;
    private javax.swing.JPanel delayP;
    private javax.swing.JPanel controlP;
    private javax.swing.JButton stepBttn;
    private javax.swing.JComboBox dataCB;
    private javax.swing.JComboBox varListCB;
    private javax.swing.JComboBox ctgCB;
    private javax.swing.JButton startBttn;
    private javax.swing.JButton pauseBttn;
    private javax.swing.JComboBox behaviorCB;
    private javax.swing.JTextField delayF;





}
